package com.clever.security.config;

import com.clever.security.dto.UserDto;
import com.clever.security.handler.LoginErrorHander;
import com.clever.security.handler.LoginSuccessfulHandler;
import com.clever.security.service.CustomerUserDetailsService;
import com.clever.security.service.UserService;
import org.springframework.cglib.proxy.NoOp;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

import javax.annotation.Resource;
import java.util.Objects;

/**
 * @author ChenWang
 * @date 2020/12/14 10:40
 * @since JDK 1.8
 *
 * 认证
 * 授权
 * 过滤
 * ant风格 ---> ant path : * ? **
 */
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Resource
    UserService userService;
    @Resource
    CustomerUserDetailsService customerUserDetailsService;
    @Resource
    LoginSuccessfulHandler loginSuccessfulHandler;
    @Resource
    LoginErrorHander loginErrorHander;
    @Resource
    PasswordEncoder passwordEncoder;
    /**
     * 过滤  --  > 安全过滤配置的方法【核心方法】
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /**
         * 1.关于登录操作的配置--更改默认登录的地址,以及成功之后的默认地址
         */

        //前后端不分离返回跳转页面，这些页面写在resource的static文件夹下
//        http.formLogin().loginPage("/login")
//                .defaultSuccessUrl("/index")
                // 放开登录界面【登录界面不拦截】
//                .permitAll();
        //前后端分离的项目返回json数据
        http.formLogin()
                //接口路径
                .loginPage("/login")
                //登录成功之后的处理
                .successHandler(loginSuccessfulHandler)
                .failureHandler(loginErrorHander)
                .permitAll();
                //此处可以用and()回到http
                //.and();
        /**
         * 2.登出配置
         */
        http.logout()
                .logoutUrl("/logout")
//                .logoutSuccessHandler()
                .permitAll();
        /**
         * 3.授权的配置信息
         */
        http.authorizeRequests()
                // 3.1 所有请求
                .anyRequest()
                // 3.2 表示所有请求必须授权认证
                .authenticated();
                // 3.3 有记住我的功能【前后端不分离有这个功能,前后端分离没有】
                //.fullyAuthenticated();
        //3.1 第二种授权的配置信息
        // 放行
        http.antMatcher("/index")
                .antMatcher("/product/**")
                .anonymous()
                .and()
                // 需要认证
                .antMatcher("/**")
                .authorizeRequests();
        /**
         *  4. 关闭跨域请求【可选配置】
         */
        http.cors().disable();

        /**
         * 5. csrf（Cross-site request forgery）跨站点请求伪造【可选配置】
         */
        http.csrf().disable();
        /**
         * 6. session的配置【可选配置】
         */
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        /**
         * 7. 关掉头部缓存的配置【可选配置】
         */
        http.headers().cacheControl().disable();
    }
    /**
     * 数据时存在内存中，还是数据库中，然后数据是否进行加密，什么加密？
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 认证的数据放在内存中,平时测试可以用，一般开发不用
//        auth.inMemoryAuthentication(); 用法
        /*auth.inMemoryAuthentication()
                .withUser("user")
                .password("123456");*/

        //从数据库中认证
        auth.userDetailsService(customerUserDetailsService).passwordEncoder(passwordEncoder);
        auth.userDetailsService(username -> {
            UserDto userDto = userService.findByUsername(username);
            if(Objects.isNull(userDto)){
                throw new RuntimeException("用户不存在");
            }
            return userDto;
        });
//        使用方法传入也可以
//        auth.userDetailsService(customerUserDetailsService).passwordEncoder(passwordEncoder());
    }

    /**
     * 处理静态资源 [一般很少用]
     *      一般在开发中过滤静态资源  web
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
    }

    /**
     * 不给信息加密
     * @return
     */
    /*@Bean
    public PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
    }*/
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

}
